package com.xiegk.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * redis 分布式锁版本 2
 * 存入键值 + lua脚本移除键
 *
 * @author xgk
 * @date 2022/2/20
 */
@Component
public class RedisLockVersion02 {
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 释放锁的 lua 脚本
     * - 解释: 获取键的内容是否 == uuid(键存储内容) 如果等于就执行del删除,否则返回 0
     */
    private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    /**
     * 锁
     *
     * @param key
     * @param timeOutSecond
     * @return 返回键存储内容
     */
    public String lock(String key, int timeOutSecond) {
        for (; ; ) {
            String uuid = UUID.randomUUID().toString(); // 存入内容
            // K key, V value, long timeout, TimeUnit unit
            Boolean exit = redisTemplate.opsForValue().setIfAbsent(key, uuid, timeOutSecond, TimeUnit.SECONDS);
            if (exit) {
                return uuid;
            }
        }
    }

    /**
     * 释放锁
     *
     * @param key  键
     * @param uuid 键的内容
     */
    public void unlock(String key, String uuid) {
        // 指定 lua 脚本，并且指定返回值类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT, Long.class);
        // RedisScript<T> script-脚本, List<K> keys-列表, Object... args-数据参数
        Long execute = redisTemplate.execute(redisScript, Collections.singletonList(key), uuid);
    }

}
